#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdint.h>
#include <string.h>
#include <assert.h>
#include <signal.h>

#include <netinet/in.h>

#include "uv.h"

typedef struct {
	uint32_t buflen;
	uint32_t capacity;
	char *buffer;
} bodydata_t;

typedef struct {
    uv_write_t req;
    uv_buf_t buf;

} write_req_t;

typedef struct {
	uv_loop_t *loop;
	uv_tcp_t *server;

} server_arg_t;


static void on_close(uv_handle_t* peer) {
  if ( peer->data)
  {
	  bodydata_t *body = (bodydata_t*)peer->data;
	  if (body->buffer)
	  {
		  free(body->buffer);
	  }
	  free(body);
  }
  free(peer);
}


static void after_shutdown(uv_shutdown_t* req, int status) {
  uv_close((uv_handle_t*)req->handle, on_close);
  free(req);
}

uv_buf_t read_alloc(uv_handle_t *handle, size_t suggested_size) {
	
	return uv_buf_init(malloc(suggested_size), suggested_size);
}

void after_write(uv_write_t *req, int status) {

	  write_req_t* wr;
	  uv_err_t err;

	  /* Free the read/write buffer and the request */
	  wr = (write_req_t*) req;
	  free(wr->buf.base);
	  free(wr);

	  if (status == 0)
		return;

	  err = uv_last_error(req->handle->loop);
	  fprintf(stderr, "uv_write error: %s\n", uv_strerror(err));

	  if (err.code == UV_ECANCELED)
		return;

	  uv_close((uv_handle_t*)req->handle, on_close);


}

void read_after(uv_stream_t *handle, ssize_t nread, uv_buf_t buf) {
	//bodydata_t *body;
	if (nread > 0)
	{
		//printf("[%d] client data is %d, \n", uv_thread_self(), *(int *)((uv_stream_t*)handle)->data);
		int fd = open("test.bin", O_WRONLY|O_CREAT |O_APPEND, 0666);
		buf.base[nread] = 0;
		//printf("s:%s\n", buf.base);
		bodydata_t *body = (bodydata_t*)handle->data;
		int capacity = body->capacity;

		if ( (body->buflen + nread )< capacity )
		{
			//strncpy(body->buffer, "###", 3);
			memcpy(body->buffer + body->buflen, buf.base, nread);
			body->buflen += nread;
		}
		else {
			//memset(body->buffer, 0x00, sizeof(body->buffer));
			printf("%s\n", body->buffer);
			memset((void *)body->buffer, 0x00, sizeof(body->buffer));
			//memset(body->buffer, 0x00, sizeof(body->buffer));
			body->buflen = 0;
			//printf("buff len %d, %p, %p\n", sizeof(body->buffer), body->buffer, body->header);
			//if (body->buffer != NULL)
			//{
			//	printf("s:%s\n", body->buffer);
			//}
			//body->buflen = 0;
			//memset(body->buffer, 0x00, sizeof(body->buffer));
		}
		if (fd)
		{
			//char connbuf[256];
			//memset(connbuf, 0x00, sizeof(connbuf));
			//snprintf(connbuf, sizeof(connbuf) - 1, "client data is %d\n",  *(int *)((uv_stream_t*)handle)->data);
			write(fd, (void *)buf.base, (size_t)strlen(buf.base));
			close(fd);
		}
		write_req_t *wr;
		wr = (write_req_t*)malloc(sizeof(*wr));
		wr->buf = uv_buf_init(buf.base, nread);
		int r = uv_write(&wr->req, handle, &wr->buf, 1, after_write);
		if (r)
		{
			if (handle->data)
			{
				//free(handle->data);
				//handle->data = NULL;
			}
			fprintf(stderr, "uv write error!\n");
			return;
		}
	}
	else {

		if (buf.base)
			free(buf.base);

		uv_err_t err = uv_last_error(handle->loop);
		if (nread == 0 || err.code == UV_EOF)
		{
			printf("buffer read size is zero!!!!!!!!!!!\n");
			return;
		}
		
		if (err.code == UV_ECONNRESET || err.code == UV_EPIPE)
		{
			printf("close handle.....\n");
			if (handle->data)
			{
				//free(handle->data);
			}
			uv_close((uv_handle_t*)handle, on_close);
			return;
		}
		//uv_err_t err = uv_last_error((uv_stream_t*)(handle->data)->loop);
		//fprintf(stderr, "uv_accept error, msg:%s!\n", uv_err_name(err));
		//fprintf(stderr, "error %s\n", uv_err_name(err));
		fprintf(stderr, "error %s, error2 %s\n", uv_err_name(err), uv_err_name(err));
		
		uv_shutdown_t *req = (uv_shutdown_t*) malloc(sizeof *req);
		uv_shutdown(req, handle, after_shutdown);

		return;
	}
}

void on_new_connection(uv_stream_t *server, int status) {
	uv_stream_t *stream;
	int r;
	if (status != 0) {
		fprintf(stderr, "Connected error, errcode:%d\n", uv_last_error(server->loop).code);
		return;
	}
	stream = (uv_stream_t*)malloc(sizeof *stream);
	r = uv_tcp_init(server->loop, (uv_tcp_t*)stream);
	if (r)
	{
		fprintf(stderr, "tcp init error!\n");
		return;
	}

	int mydata = random();
	bodydata_t *bodydata;
	bodydata = (bodydata_t *)malloc(sizeof(*bodydata));
	bodydata->buflen = 0;
	bodydata->capacity = sizeof(char) * 1024;
	bodydata->buffer = (char *)malloc(bodydata->capacity);
	memset(bodydata->buffer, 0x00, bodydata->capacity);

	stream->data = (void *)bodydata;

	r = uv_accept(server, stream);
	if (r)
	{
		uv_err_t err = uv_last_error(server->loop);
		fprintf(stderr, "uv_accept error, msg:%s!\n", uv_err_name(err));
		return;
	}
	//int uv_tcp_getpeername(uv_tcp_t* handle, struct sockaddr* name, int* namelen) 
	struct sockaddr soaddr;
	int soaddrlen = sizeof(soaddr);
	int pret = uv_tcp_getpeername((uv_tcp_t*)stream, &soaddr, &soaddrlen);
	if (pret == 0)
	{
		struct sockaddr_in *cliaddr = (struct sockaddr_in*)&soaddr;
		char clistrip[24] = {0};
		int iplen = INET_ADDRSTRLEN;
		inet_ntop(AF_INET, &cliaddr->sin_addr, clistrip, iplen);
		printf("[%u] client info:%s:%d\n", uv_thread_self(), clistrip, ntohs(cliaddr->sin_port)); 

	}
	r = uv_read_start(stream, read_alloc, read_after);
	//printf("stream data is %d\n", *(int*)stream->data);
	if (r)
	{
		uv_err_t err = uv_last_error(server->loop);
		fprintf(stderr, "uv_read_start error, msg:%s!\n", "uv_err_name(err)");
		return;
	}
	//assert(r);
}

void thread_work(void *arg) {
	
	server_arg_t *argv = (server_arg_t*)arg;

	uv_stream_t *stream = (uv_stream_t*) argv->server;
	int r = uv_listen(stream, SOMAXCONN, on_new_connection);
	if (r)
	{
		uv_err_t err = uv_last_error(argv->loop);
		fprintf(stderr, "Listen error:%s\n", uv_err_name(err));
	}

	uv_run(argv->server->loop, UV_RUN_DEFAULT);
}

int tcp4_server_main(server_arg_t *argv) {
	struct sockaddr_in addr = uv_ip4_addr("0.0.0.0", 8080);
	int r = uv_tcp_init(argv->loop, argv->server);
	if (r)
	{
		fprintf(stderr, "socket create error");
		return -1;
	}
	r = uv_tcp_bind(argv->server, addr);
	if (r)
	{
		fprintf(stderr, "Bind tcp error!\n");
		return -1;
	}
	//server_arg_t *tharg1, *tharg2;
	/*uv_thread_t thid1, thid2;
	uv_thread_create(&thid1, thread_work, (void *)argv);
	uv_thread_create(&thid2, thread_work, (void *)argv);
	
	printf("thead id is %ud, %u\n", thid1, thid2);
	uv_thread_join(&thid1);
	uv_thread_join(&thid2);*/
//thread will error:
//sockettest: src/unix/stream.c:877: uv__write_callbacks: Assertion `((&(stream->loop)->active_reqs == (&(stream->loop)->active_reqs)->prev) == 0)' failed.
//
	thread_work((void *)argv);
	//uv_stream_t *stream = (uv_stream_t*) argv->server;
	//stream->type = UV_TCP;

/*
	r = uv_listen(stream, SOMAXCONN, on_new_connection);
	if (r)
	{
		uv_err_t err = uv_last_error(argv->loop);
		fprintf(stderr, "Listen error:%s\n", uv_err_name(err));
		return -1;
	}
*/
	return 0;
}

int main(void) {
	signal(SIGPIPE, SIG_IGN);

	uv_loop_t *rloop = uv_loop_new();
	uv_tcp_t rtcpserver;
	server_arg_t serverarg;
	serverarg.loop = rloop;
	serverarg.server = &rtcpserver;

	int ret = tcp4_server_main(&serverarg);	

	uv_loop_delete(rloop);
	return 0;
}
